Repository navigation
✨ Workflow models: signed bundles, hub marketplace catalogue with nightly refresh, spoke import/publish - #17714
Conversation
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
Open PR overlap checkBase branch: Conflicts are checked with every PR merged onto the current tip of No conflicting open PRs or same-file overlaps were found. |
43c3eea to
f7e6701
Compare
|
Hive status — updated in place; edits do not notify. Full history is in the Activity Monitor. clubanderson this PR is held at Why:
Next: the hive does not push to your branch. Push a new head (or reply here); CI and the review swarm re-run automatically and the PR returns to the normal path. Evidence bundle `#17714a305b60` sha256:399199cce4dd898746af3377df56448c57c451a82b02690a8d3c2d88d2f9c8b4 — download from the dashboard or `hivectl review evidence #17714`.Review findings from HUMAN DECISION NEEDED — Security perspective, read at head a305b60 (hub 1. Spokes can impersonate the hub's crawl tier and permanently squat catalogue keys (medium)
case bundle.TierLocalPublished, bundle.TierIngestedUnreviewed:Only the hub's own refresh pipeline should ever produce that tier — it is the label the catalogue uses to mean "the hub crawled this from the CNCF corpus", and (and drop Separately, because the store is immutable and the PR adds no delete/unpublish route, any spoke can also squat 2. Spoke heartbeat bearer is sent to
|
Mutation testing⏱️ The 900s mutation budget ran out, so 3 package(s) were not fully mutated and the results below do not cover them:
❌ gremlins did not produce a report for 1 package(s), so no verdict can be derived from this run (see the job log):
1 mutant(s) in changed files are not reached by any test (NOT COVERED). A maintainer may apply the
Verdict: |
33fc753 to
8aadf20
Compare
Add pkg/workflowmodel/bundle: Export compiles a model and wraps it with its profile and a sha256 of the canonical model JSON; Encode/Decode use canonical (sorted-key) JSON; Verify recomputes the hash, recompiles the model locally and compares the profile structurally so importers never trust a published profile. Add pkg/workflowmodel/ingest/corpus embedding the CNCF survey corpus (262 projects / 299 repos) so the hub marketplace refresh and cmd/workflow-survey share one list; the survey falls back to it when a corpus directory has no projects.json. Refs #17620 Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Refs #17620 Signed-off-by: Andrew Anderson <andy@clubanderson.com>
GET /api/workflows/{name}/{version}/bundle exports a verified bundle;
POST /api/workflows/import verifies and recompiles locally before saving
(422 on verification failure). /api/marketplace/workflows proxies the
hub index with an ETag cache and serves the cached copy with stale=true
on hub outage; .../{name}/{version}/import and .../publish move bundles
between the library and the hub with the spoke's heartbeat credential;
/api/marketplace/refresh forwards an on-demand refresh. Owner-gated and
audited where the store or hub is written. Shared index types live in
pkg/workflowmodel/bundle so hub and spoke agree on the wire shape.
Refs #17620
Signed-off-by: Andrew Anderson <andy@clubanderson.com>
…us refresh Adds an immutable, file-backed catalogue under the hub data dir (marketplace/workflows/<name>/<version>.bundle.json + index.json) with routes for the index (strong ETag / 304), per-bundle fetch, publish (spoke heartbeat bearer or hub admin; curated/built-in tiers admin-only), admin on-demand refresh and a status view. The refresh walks the embedded CNCF corpus plus HIVE_HUB_MARKETPLACE_EXTRA_REPOS, records source SHA and pipeline-file hash per repo, skips unchanged repos and publishes changed ones as ingested-YYYYMMDD.N (tier ingested-unreviewed). Curated versions are never overwritten; the index flags them newerIngestAvailable. A nightly run (HIVE_HUB_MARKETPLACE_REFRESH_SCHEDULE, default 03:00 UTC) starts with the background pollers and seeds an empty catalogue once a GitHub token is present. Refs #17620 Signed-off-by: Andrew Anderson <andy@clubanderson.com>
… catalogue refresh Adds a Marketplace item to the Settings → Workflows nav that lists the hub catalogue through the spoke proxy (tier badges, search, tier filter, newer-ingest flag, reserved adoption/rating columns, stale banner when the hub is unreachable), imports entries into the local library (the spoke verifies and recompiles; nothing is bound) and lets owners publish a library model to the hub after confirmation. New files only, plus one-line hooks in config-workflows-library.js and the manifest. Refs #17620 Signed-off-by: Andrew Anderson <andy@clubanderson.com>
…/publish (RFC phase 7) Refs #17620 Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Replace the two time.Sleep polls in the hub marketplace tests with testutil.Eventually / direct cancel so the sleep ratchet stays at its baseline, and remove the unused fakeHub.down field flagged by golangci-lint. Refs #17620 Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Refs #17620 Signed-off-by: Andrew Anderson <andy@clubanderson.com>
…editor view Refs #17620 Signed-off-by: Andrew Anderson <andy@clubanderson.com>
8aadf20 to
a305b60
Compare
|
Thank you for your contribution! Your PR has been merged. We'd love to hear how your experience was: share feedback |
|
merged by Hive trusted-author auto-merge: author @clubanderson holds role owner and GitHub permission admin on repo; CI green at a305b60. |
There was a problem hiding this comment.
Intent-alignment review (head a305b60)
Checked the diff against the PR body and RFC #17620. The route/tier/verify claims in the body match the hub code (src/pkg/hub/marketplace.go — publish gated to spoke bearer + X-Hive-ID or admin, curated/built-in admin-only at the tier switch, bundle.Verify before store). Two gaps between what the PR says and what it does:
1. "Signed bundles" — nothing is signed (medium, in-scope)
The title, changelog (changelog.d/added-workflowmodel-marketplace.md:1), OpenAPI summary (dashboard/openapi.json — "Export a stored workflow model as a signed bundle"), design doc (src/docs/design/workflow-models.md, "Bundles and marketplace" section), hub-server.md (/api/marketplace/workflows/{name}/{version}/bundle row: "One immutable signed bundle") and the Publish confirm dialog (src/pkg/dashboard/static-src/js/config-workflows-marketplace.js:173, "A signed bundle (model + compiled profile + hash)") all say signed. The implementation is a content hash only: Bundle has Hash = sha256 of canonical model JSON and no key, signer, or signature field (src/pkg/workflowmodel/bundle/bundle.go:58-68), and Verify only recomputes that hash and recompiles (bundle.go:157-199). Anyone who edits a model can recompute the hash, so the bundle provides integrity-in-transit and profile consistency, not authenticity. Calling it "signed" overstates a security property to operators clicking Import/Publish.
Suggested fix: rename to "hashed"/"verified" everywhere user-facing (or add a real signature in a follow-up). E.g.:
const ok = await hiveConfirm({ title: 'Publish ' + name + '@' + version + ' to the hub?', message: 'A verified bundle (model + compiled profile + sha256 hash) is sent to the hub marketplace under this hive\'s identity as tier local-published. Catalogue entries are immutable: once published, this name@version cannot be changed, only superseded by a new version.', confirmLabel: 'Publish' });
and the same word swap in the title, changelog fragment, openapi.json summary, hub-server.md, and workflow-models.md.
2. Nightly auto-publish is outside what RFC #17620 scoped for P7 (low, in-scope, advisory)
The RFC says P7 is "Export/import bundle; hub listing. Marketplace publishing is a separate RFC" and, under Marketplace: "publishing is a human act from the library, never automatic." This PR's refreshOneRepo (src/pkg/hub/marketplace_refresh.go:247) publishes an ingested-unreviewed bundle for every changed repo in the 299-repo corpus on a nightly schedule and auto-seeds at startup when a GitHub token is present (marketplace_refresh.go:424-445). The body's "Deviations / notes" section does not call this out. Since the author owns the RFC this is likely intentional, but please either update the RFC's P7/Marketplace text or list it as a deviation in the PR body so the record matches the code.
No unrelated scope creep found; the cmd/workflow-survey corpus fallback is a reasonable consolidation for the shared list.
— hive: agent=ci-maintainer backend=copilot model=claude-fable-5.1 copilot=1.0.88
RFC phase 7 of the workflow-models RFC: a model can now leave the hive it was authored on. Three pieces — a signed bundle format, a hub-side immutable marketplace catalogue with nightly refresh from the CNCF corpus, and spoke import/publish/browse — plus a Settings → Workflows → Marketplace view.
Refs #17620
What's in it
A.
pkg/workflowmodel/bundle(new)Bundle{APIVersion, Kind, Model, Profile, Hash, CreatedAt, CreatedBy, Tier};Export(compiles, refuses on compile errors),Encode/Decode(canonical sorted-key JSON, strict),Verify(recomputes sha256 of the canonical model and recompiles locally, comparing the profile structurally — the published profile is never trusted),CanonicalHash.built-in·curated·ingested-unreviewed·local·local-published.IndexEntry/Index(reserveadoption,ratings,benchmarksas empty objects).pkg/workflowmodel/ingest/corpus(new): the CNCF survey corpus embedded (262 projects / 299 repos) so hub andcmd/workflow-surveyshare one list; the survey falls back to it when noprojects.jsonis passed.B. Spoke API —
pkg/dashboard/api_workflows_marketplace.go(new; one-line hook inapi_workflows.go)GET /api/workflows/{name}/{version}/bundlePOST /api/workflows/import(body = bundle)201 {entry, verified:true};422on verify failure; auditworkflow_importGET /api/marketplace/workflows[?refresh=1]If-None-Match+ TTL cache →{index, stale, etag, fetchedAt, error?}; hub down →502+ cached index +stale:truePOST /api/marketplace/workflows/{name}/{version}/importPOST /api/marketplace/workflows/publish {name, version}X-Hive-ID→201 {published, hash, tier}; auditworkflow_publishPOST /api/marketplace/refresh[?repo=]workflow_marketplace_refreshConfig (
pkg/config):workflow.marketplace.enabled(default true),workflow.marketplace.url(defaulthub.url),workflow.marketplace.refresh_interval(default 15m). Golden regenerated. openapi.json paths/schemas added as additive blocks.C. Hub catalogue —
pkg/hub/marketplace{,_store,_refresh}.go(new)marketplace/workflows/<name>/<version>.bundle.json+index.json+refresh/<owner>__<repo>.json. Immutable: identical republish is a no-op, different content under an existingname@version→409. ETag = sha256 of index bytes. Index rebuilds from bundles ifindex.jsonis missing.GET /api/marketplace/workflows(ETag/304),GET /api/marketplace/workflows/{name}/{version}/bundle(immutable cache headers),POST /api/marketplace/workflows(publish; spoke heartbeat bearer +X-Hive-ID, or hub-admin session with CSRF evidence; publisher may assertlocal/local-published/ingested-unreviewed,curated/built-inneed admin; hub re-verifies),POST /api/marketplace/refresh[?repo=](admin; background202or synchronous single repo),GET /api/marketplace/status.HIVE_HUB_MARKETPLACE_EXTRA_REPOS; per repo fetch default-branch tarball via the hub's GitHub identity →ingest.ReadTree→ files hash; unchanged → no-op; changed →ingest.Lifecycle→bundle.Export→ publishingested-YYYYMMDD.Ntieringested-unreviewedwith source SHA. Curated never overwritten, only flaggednewerIngestAvailable. Sequential crawl with pause, honours GitHub rate-limit headers. Nightly atHIVE_HUB_MARKETPLACE_REFRESH_SCHEDULE(default03:00UTC; empty disables) + on demand; seeds once at startup (goroutine) when the catalogue is empty and a GitHub token is present.HIVE_HUB_MARKETPLACE_DISABLED→ routes 404.docs/api-reference/hub-server.md.D. UI —
static-src/js/config-workflows-marketplace.js+css/workflows-marketplace.css(new)Marketplace item in the Workflows nav; table with tier badges, search, tier filter (with counts),
newer ingest availableflag, reserved Adoption/Rating columns (—), Import (verified + compiled locally → toast → opens the library entry), Reload, Refresh catalogue (owner), stale banner; Publish on library rows and detail (owner,hiveConfirm). Pure render functions + node test. CSS tokens only; notoLocaleString; no forced downloads.E. Docs
"Bundles and marketplace (RFC phase 7)" appended to
docs/design/workflow-models.md; hub routes inhub-server.md; changelog fragment.Validation
gofmt,go vetclean on touched packages.workflowmodel/bundle92.4%,ingest/corpus96.8%,config90.1%,hub88.8% (floor 87),dashboard86.5% (floor 84),webstatic98.6%.go test ./pkg/dashboard/ ./pkg/dashboard/webstatic/ ./pkg/hub/ ./pkg/workflowmodel/... ./pkg/config/— all pass except the two known macOS-only failures (TestConfigExportReportsPersistedPath,TestKnowledgeDrainLegacyMemory).webstatic/gen -check,check-api-reference-citations.sh --fix(0 drifted),check-changelog-fragment.sh v5OK.Deviations / notes
workflow.marketplace.*(nested inWorkflowConfig) rather than a top-level block.HIVE_HUB_MARKETPLACE_*), matching how the hub is configured today; no hub YAML.X-Hive-IDheader.hub.url(https://hive.hivecommons.devin config); nothing in the repo referenceshub.hivecommons.dev.adoption/ratings/benchmarksare reserved and empty; nothing collects them yet.